/**
 * 
 */
package com.netscenery.yang.model;

import java.util.ArrayList;

import org.jdom2.Attribute;
import org.jdom2.Element;

import com.netscenery.util.ErrTag;
import com.netscenery.util.Link;
import com.netscenery.util.QName;
import com.netscenery.util.XMLTag;
import com.netscenery.yang.YangKeyword;
import com.netscenery.yang.YangStatement;
import com.netscenery.yang.designer.MapInfo;
import com.netscenery.yang.parser.YangParserException;

/**
 * @author yanzi
 *
 */
public class Augment extends Entity {
	private String when;
	private DataDefEntity dataDefs;
	//private Link<Node> nodes;
	private ArrayList<String> if_features;
	//private ArrayList<Anyxml> anyxmls;
	//private ArrayList<Uses> usess;
	private Node targetNode;
	
	

	/**
	 * 
	 */
	public Augment() {
		// TODO Auto-generated constructor stub
	}

	/**
	 * @param env
	 * @param name
	 * @throws ModelException 
	 */
	public Augment(Env env, String name) throws ModelException {
		super(env, name);
		dataDefs = new DataDefEntity();
		// TODO Auto-generated constructor stub
	}
	
	
	public Node getTargetNode() {
		return targetNode;
	}

	public void setTargetNode(Node targetNode) {
		this.targetNode = targetNode;
	}

	public Node getCurNode(){
		if (null == name){
			return null;
		}
		
		if (env.getParent() instanceof Module){
			
			return getNodeByAbsPath(env, name);
		}
		else if (env.getParent() instanceof Uses){
			return getNodeByDescendantPath(env, env.getParent(), name);
		}
		else {
			return null;
		}
	}
	public DataDefEntity getDataDefs(){
		return dataDefs;
	}
	@Override
	public boolean checkName(String name) {
		if (null == name){
			return false;
		}
		
		if (env.getParent() instanceof Module){
			//������absolute-schema-node����ʽ
			if (!isAbsolutePath(env, name)){
				return false;
			}
			
		}
		else if (env.getParent() instanceof Uses){
			//������descent-schema-node����ʽ
			if (!isDescendantPath(env, name)){
				return false;
			}
		}
		else {
			return false;
		}
		return true;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#validate()
	 */
	@Override
	public Validator validate() {
		Validator validator = new Validator();
		if (!checkName(name)){
			validator.setIsValid(false);
			validator.addMsg(new Message(this, ErrTag.ERROR, ErrorCode.COMMON_INVALIDNAME));
		}
		if (env.getParent() instanceof Module){
		
			
			//�������ҵ�·��ָ��Ľڵ�
			if(null == getNodeByAbsPath(env, name)){
				validator.setIsValid(false);
				validator.addMsg(new Message(this, ErrTag.ERROR, ErrorCode.COMMON_INVALIDNAME));
			}
		}
		else if (env.getParent() instanceof Uses){
			
			
			//�������ҵ�·��ָ��Ľڵ�
			if(null == getNodeByDescendantPath(env, env.getParent(), name)){
				validator.setIsValid(false);
				validator.addMsg(new Message(this, ErrTag.ERROR, ErrorCode.COMMON_INVALIDNAME));
			}
		}
		Validator sValidator = super.validate();
		validator.copy(sValidator);
		
		return validator;
	}



	public String getWhen() {
		return when;
	}

	public void setWhen(String when) {
		this.when = when;
	}

	public int getDataDefsNumber(){
		if (null == this.dataDefs){
			return 0;
		}
		return this.dataDefs.getDataDefsNumber();
	}
	
	public DataDefElement createDataDef(String type, String name) throws ModelException{
		Env subEnv = newSubEnv();
		if (type.equals(YangKeyword.CASE.getKeyword())
				|| type.equals(YangKeyword.CHOICE.getKeyword())
				|| type.equals(YangKeyword.CONTAINER.getKeyword())
				|| type.equals(YangKeyword.LEAF.getKeyword())
				|| type.equals(YangKeyword.LEAFLIST.getKeyword())
				|| type.equals(YangKeyword.LIST.getKeyword())
				){
			return Node.create(subEnv, type, name);
		}
		else if (type.equals(YangKeyword.ANYXML.getKeyword())){
			return new Anyxml(subEnv,name);
		}
		else if (type.equals(YangKeyword.USES.getKeyword())){
			return new Uses(subEnv,name);
		}
		else {
			return null;
		}
	}
	
	public boolean addDataDef(DataDefElement n){
		return dataDefs.addDataDef(n);
	}

	public DataDefElement getDataDef(int index){
		return dataDefs.getDataDef(index);
	}

	public Node getNode(String name){
		return dataDefs.getNode(name);
	}
	

	public Node removeNode(String nodeName){
		return dataDefs.removeNode(nodeName);
	}

	public DataDefElement removeDataDef(int index){
		return dataDefs.removeDataDef(index);
	}

	public boolean moveDataDef(DataDefElement src,DataDefElement dst){
		return dataDefs.moveDataDef(src, dst);
	}
	/**
	 * ��ָ��˳��Ľڵ��ƶ���Ŀ��˳��ڵ�֮��
	 * @param srcIndex
	 * @param dstIndex
	 * @return
	 */
	public boolean moveDataDef(int srcIndex,int dstIndex){
		return dataDefs.moveDataDef(srcIndex, dstIndex);
	}
	/**
	 * �����ƻ�ȡuses
	 * @param name
	 * @return
	 * @see com.netscenery.model.Uses
	 */
	public Uses getUses(String name){
		return dataDefs.getUses(name);
	}

	/**
	 * ������ɾ��uses
	 * @param name
	 * @return Uses
	 * @see com.netscenery.model.Uses
	 */
	public Uses removeUses(String name){
		return dataDefs.removeUses(name);
	}
	public boolean checkIf_feature(String feature){
		if (null == feature){
			return false;
		}
		Module m = env.getCurModule();
		if (null == m){
			return false;
		}
		QName qFeature = new QName(feature);
		if (qFeature.isQName()){
			String prefix = qFeature.getPrefix();
			String localName = qFeature.getLocalName();
			if (null == prefix || null == localName){
				return false;
			}
			Module imp = m.getModule(prefix);
			if (null == imp ){
				return false;
			}
			
			if (null == imp.getFeature(localName)){
				return false;
			}
	
		}
		else {
			if (null == m.getFeature(feature)){
				return false;
			}
		}
		return true;
	}
	public boolean addIf_feature(String feature){
		if (null == feature){
			return false;
		}
		if (false == checkIf_feature(feature)){
			return false;
		}
		if (null == if_features){
			if_features = new ArrayList<String>(1);
		}
		return if_features.add(feature);
	}
	
	public String getIf_feature(int index){
		if (null == if_features){
			return null;
		}
		return if_features.get(index);
	}
	
	public String removeIf_feature(int index){
		if (null == if_features){
			return null;
		}
		return if_features.remove(index);
	}
	
	public int getIf_featuresNumber(){
		if (null == if_features){
			return 0;
		}
		
		return if_features.size();
	}
	

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#toYang()
	 */
	@Override
	public YangStatement toYang() {
		YangStatement statement = new YangStatement(YangKeyword.AUGMENT.getKeyword(),this.name);
		if (!isNullString(this.when)){
			YangStatement when = new YangStatement(YangKeyword.WHEN.getKeyword(),this.when);
			statement.addChild(when);
		}
		
		if (null != if_features){
			int size = this.getIf_featuresNumber();
			
			for (int i = 0; i < size; i++){
				String feature = this.getIf_feature(i);
				if (null != feature){
					YangStatement  featureState = 
							new YangStatement(
									YangStatement.YANG_STATEMENT_IFFEATURE
							        ,feature);
					statement.addChild(featureState);
				}
			}
		
		}
		if (!isNullString(getDescription())){
			YangStatement descr = new YangStatement(YangKeyword.DESCRIPTION.getKeyword(),getDescription());
			statement.addChild(descr);
		}
		if (null != getReference()){
			YangStatement ref = new YangStatement(YangStatement.YANG_STATEMENT_REFERENCE,this.getReference());
			statement.addChild(ref);
		}
		
		if (Status.CURRENT != getStatus()){
			YangStatement status = new YangStatement(YangStatement.YANG_STATEMENT_STATUS,this.getStatus().getStatus());
			statement.addChild(status);
		}



		/*
		 * nodes
		 */
		if (null != this.dataDefs){
			int size = this.getDataDefsNumber();
			for (int i = 0; i < size; i++){
				DataDefElement n = this.getDataDef(i);
				if (null != n){
					YangStatement nodestat = n.writeYang();
					if (null == nodestat){
						continue;
					}
					statement.addChild(nodestat);
				}
			}
		}
		return statement;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#toYin()
	 */
	@Override
	public Element toYin() {
		Element element = new Element(YangKeyword.AUGMENT.getKeyword(),env.getNs());
		Attribute target = new Attribute("target-node",this.name);
		element.setAttribute(target);
		
		if (!isNullString(this.when)){
			Element when = new Element(YangKeyword.WHEN.getKeyword(),env.getNs());
			Attribute condition = new Attribute("condition",this.when);
			when.setAttribute(condition);
			element.addContent(when);
		}		
		if (null != this.if_features){
			int size = this.getIf_featuresNumber();
			
			for (int i = 0; i < size; i++){
				String feature = this.getIf_feature(i);
				if (null != feature){
					Element  featureState = 
							new Element(YangKeyword.IFFEATURE.getKeyword(),env.getNs());
					Attribute fname = new Attribute(XMLTag.XML_TAG_NAME,feature);
					featureState.setAttribute(fname);
					element.addContent(featureState);
				}
			}
			
		}
		
		if (!isNullString(getDescription())){
			Element descr = new Element(YangStatement.YANG_STATEMENT_DESCRIPTION,env.getNs());
			Element text = new Element(XMLTag.XML_TAG_TEXT,env.getNs());
			text.setText(getDescription());
			descr.addContent(text);
			element.addContent(descr);
		}
		if (null != getReference()){
			Element ref = new Element(YangStatement.YANG_STATEMENT_REFERENCE,env.getNs());
			Element text = new Element(XMLTag.XML_TAG_TEXT,env.getNs());
			text.setText(getReference());
			ref.addContent(text);
			element.addContent(ref);
		}
		
		if (Status.CURRENT != getStatus()){
			Element status = new Element(YangStatement.YANG_STATEMENT_STATUS,env.getNs());
			Attribute value = new Attribute(YangKeyword.VALUE.getKeyword(),this.getStatus().getStatus());
			status.setAttribute(value);
			element.addContent(status);
		}
		
		//nodes
		if (null != this.dataDefs){
			int size = this.getDataDefsNumber();
			for (int i = 0; i < size; i++){
				DataDefElement n = this.getDataDef(i);
				if (null != n){
					Element node = n.writeYin();
					if (null == node){
						continue;
					}
					element.addContent(node);
				}
			}
		}		
		return element;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#toXML()
	 */
	@Override
	public Element toXML() {
		// TODO Auto-generated method stub
		return null;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#readXML(org.jdom2.Element)
	 */
	@Override
	public int readXML(Element element) throws ModelException {
		// TODO Auto-generated method stub
		return 0;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#readYin(org.jdom2.Element)
	 */
	@Override
	public int readYin(Element statement) throws ModelException {
		if (null == statement){
			return MODELELEMENT_OK;
		}

		// ����
		if (statement.getName()
				.equals(YangStatement.YANG_STATEMENT_DESCRIPTION)) {
			Element text = statement.getChild(XMLTag.XML_TAG_TEXT, env.getNs());
			if (null == text) {
				return MODELELEMENT_FAIL;
			}
			this.setDescription(text.getText());
		}
		if (statement.getName().equals(YangKeyword.WHEN.getKeyword())) {
			this.setWhen(statement.getAttributeValue("condition", env.getNs()));
		}
		// reference
		else if (statement.getName().equals(
				YangStatement.YANG_STATEMENT_REFERENCE)) {

			Element text = statement.getChild(XMLTag.XML_TAG_TEXT, env.getNs());
			if (null == text) {
				return MODELELEMENT_FAIL;
			}
			this.setReference(text.getText());
		}
		// status
		else if (statement.getName()
				.equals(YangStatement.YANG_STATEMENT_STATUS)) {
			String value = statement.getAttributeValue(YangKeyword.VALUE
					.getKeyword());
			if (null == value) {
				return MODELELEMENT_FAIL;
			} else {
				this.status = Status.getStatus(value);
			}

		}
		
		// if-feature
		else if (statement.getName().equals(
				YangStatement.YANG_STATEMENT_IFFEATURE)) {
			String value= statement.getAttributeValue(XMLTag.XML_TAG_NAME);
			if (null == value){
				return MODELELEMENT_FAIL;
			}
			this.addIf_feature(value);
		}
		//nodes
		else if (statement.getName().equals(YangStatement.YANG_STATEMENT_CONTAINER)
				|| statement.getName().equals(YangStatement.YANG_STATEMENT_CASE)
				|| statement.getName().equals(YangStatement.YANG_STATEMENT_CHOICE)
				|| statement.getName().equals(YangStatement.YANG_STATEMENT_LEAF)
				|| statement.getName().equals(YangStatement.YANG_STATEMENT_LEAFLIST)
				|| statement.getName().equals(YangStatement.YANG_STATEMENT_LIST)
				|| statement.getName().equals(YangStatement.YANG_STATEMENT_USES)
				|| statement.getName().equals(YangStatement.YANG_STATEMENT_ANYXML)){

			Env subEnv = newSubEnv();
			DataDefElement n = (DataDefElement) ModelElement.parseYin(subEnv, statement);
			this.addDataDef(n);
		}   
		return MODELELEMENT_OK;	
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#readYANG(com.netscenery.yang.YangStatement)
	 */
	@Override
	public int readYANG(YangStatement subStatement) throws YangParserException, ModelException {
		if (null == subStatement){
			return MODELELEMENT_OK;
		}
		String value = subStatement.getArgument();
		
		//����
		if (subStatement.getKeyword().equals(YangStatement.YANG_STATEMENT_DESCRIPTION)){
			this.setDescription(value);
		}
		//when
		if (subStatement.getKeyword().equals(YangKeyword.WHEN.getKeyword())){
			this.setWhen(value);
		}
	    //reference
		else if (subStatement.getKeyword().equals(YangStatement.YANG_STATEMENT_REFERENCE)){
			this.setReference(value);
		}
	    //status
		else if (subStatement.getKeyword().equals(YangStatement.YANG_STATEMENT_STATUS)){
			this.setStatus(Status.getStatus(value));
		}
		// if-feature
		else if (subStatement.getKeyword().equals(
				YangStatement.YANG_STATEMENT_IFFEATURE)) {
			this.addIf_feature(value);
		}

		//nodes
		else if (subStatement.getKeyword().equals(YangStatement.YANG_STATEMENT_CONTAINER)
				|| subStatement.getKeyword().equals(YangStatement.YANG_STATEMENT_CASE)
				|| subStatement.getKeyword().equals(YangStatement.YANG_STATEMENT_CHOICE)
				|| subStatement.getKeyword().equals(YangStatement.YANG_STATEMENT_LEAF)
				|| subStatement.getKeyword().equals(YangStatement.YANG_STATEMENT_LEAFLIST)
				|| subStatement.getKeyword().equals(YangStatement.YANG_STATEMENT_LIST)
				|| subStatement.getKeyword().equals(YangStatement.YANG_STATEMENT_USES)
				|| subStatement.getKeyword().equals(YangStatement.YANG_STATEMENT_ANYXML)
				){
			Env subEnv = newSubEnv();
			DataDefElement n = (DataDefElement) ModelElement.parseYANG(subEnv, subStatement);
			this.addDataDef(n);
		}   
		
		return MODELELEMENT_OK;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#isFieldMandatory(java.lang.String)
	 */
	@Override
	public boolean isFieldMandatory(String field) {
		// TODO Auto-generated method stub
		return false;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#getMapInfo()
	 */
	@Override
	public MapInfo getMapInfo() throws SecurityException, NoSuchMethodException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void update() {
		// TODO Auto-generated method stub
		if(isUpdated()){
			return;
		}
	}

}
